查看原文
其他

【综述专栏】网络模型加速——轻量化网络

在科学研究中,从方法论上来讲,都应“先见森林,再见树木”。当前,人工智能学术研究方兴未艾,技术迅猛发展,可谓万木争荣,日新月异。对于AI从业者来说,在广袤的知识森林中,系统梳理脉络,才能更好地把握趋势。为此,我们精选国内外优秀的综述文章,开辟“综述专栏”,敬请关注。

来源:知乎—Mrs.li

地址:https://zhuanlan.zhihu.com/p/357348544

深度神经网络模型被广泛应用在图像分类、物体检测,目标跟踪等计算机视觉任务中,并取得了巨大成功。随着时代发展,人们更加关注深度神经网络的实际应用性能,人工智能技术的一个趋势是在边缘端平台上部署高性能的神经网络模型,并能在真实场景中实时(>30帧)运行,如移动端/嵌入式设备,这些平台的特点是内存资源少,处理器性能不高,功耗受限,这使得目前精度最高的模型根本无法在这些平台进行部署和达到实时运行。由于存储空间和算力资源限制,神经网络模型在移动设备嵌入式设备上的存储与计算仍然是一个巨大的挑战。

图1

轻量化网络旨在保持模型精度基础上近一步减少模型参数量和复杂度,逐渐成为计算机视觉中的一个研究热点。
轻量化网络既包含了对网络结构的探索,又有例如知识蒸馏,剪枝等模型压缩技术的运用,推动了深度学习技术在移动端,嵌入式端的应用落地,在智能家居,安防,自动驾驶,视频监控等领域都有重要贡献。

图2

轻量化的思想对工业界和学术界都有深刻启发,是面试中的高频问题,同时也是视觉领域理论研究的热点,倍受关注,有广阔的领域值得去探索学习。
当前物体检测结构大都依赖使用卷积网络进行特征提取,即 Backbone,比如AlexNet的8层、VGGNet的19层,GoogleNet的22层,乃至于ResNet的152层 等优秀的基础网络,随着网络深度的不断提升,网络模型性能的确是提高了,但是这些网络往往计算量巨大,依赖这些基础网络的检测算法很难达到实时运行的要求,尤其是在 ARM、FPGA以及 ASIC 等计算力有限的移动端硬件平台。逐渐呈现了一种在移动设备平台上“算不好”,穿戴设备上“算不了”,数据中心“算不起”的无奈场面,如图3所示(图片来自我一个同学的PPT内容)因此如何将物体检测算法加速到满足工业应用要求,一直是关键性问题。
图3 深层网络模型难以实际应用
从轻量化网络结构定义来看,我们可以将轻量化网络分为轻量化网络结构设计模型压缩两大类,其中模型压缩又可细分为知识蒸馏、剪枝、量化、低秩分解四个小类别,如图4所示。
图4 轻量化网络
1. 轻量化设计: 从模型设计时采用一些轻量化的思想,例如采用深度可分离卷积、分组卷积等轻量卷积方式、减少卷积过程的计算量,此外采用全局池化来取代全连接层、利用 1 x 1 卷积实现特征的通道降维,也可以降低模型的计算量,这两点在众多网络中得到应用。
2. 知识蒸馏:大的模型拥有更强的拟合能力和泛化能力,而小的模型拟合能力较弱,且容易造成过拟合,使用大模型指导小模型训练保留大模型的有效信息,实现知识蒸馏。
3. 网络剪枝: 在卷积网络成千上万的权重中,存在着大量接近 0 的参数,这些属于冗余的参数,去掉后模型也可以基本达到相同的表达能力,因此众多众多研究者从此为出发点,搜索网络中的冗余卷积核、将网络稀疏化,称之为网络裁剪。具体讲,网络剪枝有训练中稀疏与训练后剪枝两种。
4. 量化:是指将网络模型中高精度的参数量化为低精度的参数,从而加速计算的方法。高精度的模型参数拥有更大的动态变化范围,能够表达更丰富的参数空间,因此训练中通常使用 32 位浮点数 (单精度) 作为网络参数模型。训练完后成为了减小模型大小,通常将 32 未浮点数量化为 16 位浮点数的半精度,甚至 int8的整型,0 与 1 的 二值类型。
5. 低秩分解:由原始网络参数中存在大量冗余,除了剪枝的方法外,我们还可以利用 SVD 分解和 PQ 分解,将原始张量分解为低秩的若干张量,以减少卷积的计算,提升前向速度。

01

模型评价指标
我们常常从以下两个评价指标来衡量网络模型的轻量级,一个是网络参数量、另一个是浮点运算数(floating point operations),也就是计算量,可以用来衡量模型的复杂度。
Params——网络参数量
对于输入为的输入图像,卷积核大小为,得到输出的特征图大小为的卷积操作,其参数量为:

FLOPs——浮点运算数
对于输入为的输入图像,卷积核大小为,得到输出的特征图大小为的卷积操作,其浮点运算数为:

要注意的是:一般来说,网络模型参数量和 浮点运算数越小,模型的速度越快,但是衡量模型的快慢不仅仅是参数量、计算量的多少,还有内存访问的次数多少相关,也就是和网络结构本身相关。

02

轻量化网络
除了一些模型压缩的方法,研究人员更加重视手工设计轻量高效的卷积神经网络架构,能有效保证模型精度的同时大大减少参数,一直是在嵌入式平台和移动端应用最为广泛的一种方法。近几年来也逐渐提出了很多优秀的轻量化网络。
MobileNetv1将常规卷积替换为深度可分离卷积,包含了深度卷积(depth-wise)和点态卷积(point-wise),假设采用的是3x3的卷积核,一般可达到8-9倍的加速,而精度不会损失太多。
MobileNetv1采用的是类似VGG的简单堆叠的方式,更为先进的方法是加入ResNet的shortcut的链接方式,MobileNetv2就借鉴了这种思想,应用了invered residual block,与Residual block相比,有如下的区别:1)、通道数:两边窄中间宽;2、3x3卷积改成了Depthwise Conv;3、去掉了最后的ReLU(ReLU会使得一些神经元失活,而高维的ReLU可以保留低维特征的完整信息,同时又不失非线性。所以采用中间宽+ReLU的方式来保留低维输入的信息)。MobileNetv3采用了网络结构搜索(NAS)的方法,得到一个高效的网络结构,先通用NAS算法,优化每一个block,得到大体的网络结构,然后使用NetAdapt 算法来确定每个filter的channel的数量,此外并结合一些必要的trick,包含
1)、引入SE(squeeze and excitation)结构,并将SE结构放在了depthwise之后;
2)、对网络结构的尾部部分进行了简化修改;
3)、修改了channels;
4)、非线性激活的改变,使用h-swish替换swish。
SqueezeNet是公认的轻量级模型设计最早期的工作之一,通过堆叠包含了squeeze部分和expand部分在内的fire module大大减少了参数。ShuffleNet利用group convolution 和channel shuffle两个操作设计卷积神经网络模型,使得模型参数得到大大的较少,加速了模型的推理。Xception与MobileNet同样采用了深度可分离卷积,主要是在Inception v3的基础上引入了depthwise separable convolution。GhostNet通过廉价且高效的线性变换操作生成了更多的Ghost feature map,Sandglass(MobileNeXt)翻转了逆残差模块(inverted residual block) ,缓解了信息的丢失。


03

SqueezeNet(最早公布时间:2016.02,发表情况:ICLR-2017)
SqeezeNet在ImageNet上实现了和AlexNet相同的正确率,但是只使用了1/50的参数。更进一步,使用模型压缩技术,可以将SqueezeNet压缩到0.5MB,这是AlexNet的1/510,SqueezeNet的模型压缩使用了3个策略:
  •   卷积替换成  卷积:通过这一步,一个卷积操作的参数数量减少了9倍;
  • 减少  卷积的通道数:一个  卷积的计算量是  (其中    分别是输入Feature Map和输出Feature Map的通道数),作者认为这样计算量过于庞大,因此希望将    减小以减少参数数量;
  • 将下采样后置:作者认为较大的Feature Map含有更多的信息,因此将降采样往分类层移动。注意这样的操作虽然会提升网络的精度,但是这样也会增加网络的计算量。
SqueezeNet是由若干个Fire模块结合卷积网络中卷积层,降采样层,全连接等层组成的。一个Fire模块由Squeeze部分和Expand部分组成。
Squeeze部分是一组连续的  卷积组成,主要对feature map的通道数进行改变,减少通道数,对立方体的feature map进行切片。这是一个几乎在轻量化网络中都要使用到的手段,对通道压缩。
Expand部分则是由一组连续的  卷积和一组连续的  卷积cancatnate组成,因此  卷积需要使用same卷积,Fire模块的结构见图

图5 SqueezeNet的Fire模块

Fire模块的Pytorch代码实现:
class Fire(nn.Module):
def __init__(self, inplanes, squeeze_planes, expand1x1_planes, expand3x3_planes): super(Fire, self).__init__() self.inplanes = inplanes self.squeeze = nn.Conv2d(inplanes, squeeze_planes, kernel_size=1) self.squeeze_activation = nn.ReLU(inplace=True) self.expand1x1 = nn.Conv2d(squeeze_planes, expand1x1_planes, kernel_size=1) self.expand1x1_activation = nn.ReLU(inplace=True) self.expand3x3 = nn.Conv2d(squeeze_planes, expand3x3_planes, kernel_size=3, padding=1) self.expand3x3_activation = nn.ReLU(inplace=True)
def forward(self, x): x = self.squeeze_activation(self.squeeze(x)) return torch.cat([ self.expand1x1_activation(self.expand1x1(x)), self.expand3x3_activation(self.expand3x3(x))        ], 1)
SqueezeNet最终就是类似VGG一样,简单的堆叠上面的Fire 模块得到整个网络模型。


04

MobileNetv1(最早公布时间:2016.04,发表情况:CVPR-2017)
MobileNet是基于深度级可分离卷积构建的网络,其实这种结构最早是出现在GoogleNet v3的inception中,它是将标准卷积拆分为了两个操作:深度卷积(depthwise convolution) 和 逐点卷积(Pointwise convolution)。
Depthwise convolution和标准卷积不同,对于标准卷积其卷积核是用在所有的输入通道上(input channels)。Depthwise convolution针对每个输入通道采用不同的卷积核,就是说一个卷积核对应一个输入通道,所以说Depthwise convolution是depth级别的操作。
Pointwise convolution其实就是普通的卷积,只不过其采用1x1的卷积核。
标准卷积:
同时进行通道和空间上的卷积操作。

图6 标准卷积

Depthwise convolution:
Depthwise Convolution的一个卷积核负责一个通道,一个通道只被一个卷积核卷积,如图7所示。上面所提到的标准卷积每个卷积核是同时操作输入图片的每个通道。
图7 深度卷积
Pointwise convolution:
Pointwise Convolution的运算与常规卷积运算非常相似,它的卷积核的尺寸为 1×1×M,M为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map。如图8所示。
图8 点态卷积
标准的卷积层将作为输入  特征图和生成一个特征图,利用卷积核卷积  大小,这里我们忽略bias的计算量部分,计算成本是:

但如果我们使用深度卷积,计算代价为:

通过比较可以得出:

一般来说, feature map的输出通道  会比较大,即可以忽略不记,如果我们在深度上使用  的可分离卷积,计算量将比标准卷积少8倍左右。
深度可分离Pytorch代码实现:

图9

想要了解 Depthwise Conv 这个计算,它的核心在于逐点卷积,在nn.Conv2d的参数中有groups这个参数,默认是groups=1,意识是分组计算,等于一是就是普通的卷积,如果等于其它的值,那就是分组卷积,如果设置为groups = input_channels,就是深度可分离卷积的depthwise conv
def _conv_dw(self, in_channels, out_channels, stride): return nn.Sequential( # DepthWise Convolution,groups = in_channels nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=stride, padding=1, groups=in_channels, bias=False), nn.BatchNorm2d(in_channels), nn.ReLU(), # PointWise Convolution, kernel_size = 1 nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(), )
对比标准卷积:Conv+BN+ReLU
def _conv_st(self, in_channels, out_channels, stride): return nn.Sequential( nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False), nn.BatchNorm2d(out_channels), nn.ReLU(), )
  • MobileNetv2(2018)
  • MobileNetv3(2019)
  • ShuffleNetv1(最早公布时间:2016.06,发表情况:CVPR-2017)
  • ShuffleNetv2(2018)
  • Xception(最早公布时间:2016.10,发表情况:N/A)
  • GhostNet(CVPR-2020)
GhostNet是出自于华为诺亚方舟实验室的一篇2020CVPR的工作,是一篇不可多得的轻量级高效网络架构,其实现原理简单,效果确极好,正所谓大道至简,如此简单高效的方式也只是我如此青睐它的一个原因。那具体它怎么实现的呢?
首先,华为诺亚方舟的研究员们将经过ResNet-50的第一个残差块生成的特征图进行可视化,在一个训练好的深度神经网络中,通常会包含丰富甚至冗余的特征图,以保证对输入数据有全面的理解。如图所示,每一个特征图都对应学习了原始图片中的某一个特征,每一个特征图的生成也对应了一个卷积操作,仔细观察我们会发现,这些特征图中会有两两相似的特征图,可以认为其中一个特征图是另一个的“幻影”,比如下图中对应的 之间的两个之间的特征图,既然这些特征图之间这么相似,难道只能通过卷积生成这些特征图?我们思考是否可以通过一种更为简单的方式来生成这些特征图,其实对于这类特征图,我们可以直接通过廉价的方式——线性变换的方式(对应下图中 ),so,就这么简单。
  • Sandglass(MobileNeXt,2020)
参考
https://www.sohu.com/a/402029810_787107
https://blog.csdn.net/weixin_43862398/article/details/104217905
https://zhuanlan.zhihu.com/p/80041030

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。


“综述专栏”历史文章


更多综述专栏文章,

请点击文章底部“阅读原文”查看



分享、点赞、在看,给个三连击呗!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存